#ifndef CSE168_IMAGE_H_INCLUDED
#define CSE168_IMAGE_H_INCLUDED

#include "Vector3.h"
#include "rgbe.h"

class Image
{
public:
    struct Pixel
    {
        unsigned char r, g, b;
        Pixel(unsigned char ir, unsigned char ig, unsigned char ib) {set(ir, ig, ib);}
        Pixel() : r(0), g(0), b(0) {}
        void set(unsigned char ir, unsigned char ig, unsigned char ib) {r = ir; g = ig; b = ib;}
    };

    Image();
    ~Image();

    void resize(int width, int height);
    void setPixel(int x, int y, const Vector3& p);
    void setPixel(int x, int y, const Pixel& p);

	bool getPixel(int x, int y, Vector3& p) const;
	bool getPixel(int x, int y, Pixel &p) const;
	bool getRgb(float x, float y, Vector3 &rgb) const;

    void draw();
    void drawScanline(int y);
    void clear(const Vector3& c);
    void writePPM(char* pcFile); // write data to a ppm image file
    void writePPM(char *pcName, unsigned char *data, int width, int height);
	void readPPM(char *pcFile); // read data from a ppm image file

    unsigned char* getCharPixels()  {return (unsigned char*)m_pixels;}
	Pixel *getPixels()				{return m_pixels;}
    int width() const               {return m_width;}
    int height() const              {return m_height;}

private:
    Pixel* m_pixels;
    int m_width;
    int m_height;
	float m_scaleFactor;
};

extern Image * g_image;
extern HdrImage * g_hdrImage;

class HdrImage
{
public:
	struct HdrPixel
	{
        float r, g, b;
        HdrPixel(float ir, float ig, float ib) {set(ir, ig, ib);}
        HdrPixel() : r(0), g(0), b(0) {}
        void set(float ir, float ig, float ib) {r = ir; g = ig; b = ib;}
    };

    HdrImage();
    ~HdrImage();

    void resize(int width, int height);
    void setPixel(int x, int y, const Vector3& p);
    void setPixel(int x, int y, const HdrPixel& p);
private:
    void setHdrPixel(int x, int y, const Vector3& p); // internal methods that only change
    void setHdrPixel(int x, int y, const HdrPixel& p); // one copy of image
    void setLdrPixel(int x, int y, const Vector3& p); // internal methods that only change
    void setLdrPixel(int x, int y, const HdrPixel& p);
    void setLdrPixel(int x, int y, const Image::Pixel& p);
public:
	
	bool getHdrPixel(HdrPixel *pixels, int x, int y, HdrPixel &hp) const;
	bool getHdrPixel(int x, int y, Vector3& hp) const;
	bool getHdrPixel(int x, int y, HdrPixel &hp) const;
	bool getLdrPixel(int x, int y, Vector3& lp) const;
	bool getLdrPixel(int x, int y, Image::Pixel &lp) const;
	bool getRgb(float x, float y, Vector3 &rgb) const;

    void draw();
    void drawScanline(int y);
    void clear(const Vector3& c);
    void writePPM(char* pcFile); // write data to a ppm image file
    void writePPM(char *pcName, unsigned char *data, int width, int height);
	void writeHDR(char *hdrName);
    void readPPM(char* pcFile); // read data from a ppm image file
	void readHDR(char *hdrName); // read from an hdr (rgbe) image file


    float *getFloatPixels()			{return (float *)m_hdrPixels;}
	unsigned char *getCharPixels()	{return m_image.getCharPixels();}
	Image::Pixel *getLdrPixels()	{return m_image.getPixels();}
    int width() const               {return m_width;}
    int height() const              {return m_height;}
	Image *getLdrImage()			{return &m_image;}
	
	
	void setToneMapStrength(float strength) { m_toneMapStrength = strength; }
	void localToneMap(float strength=0.3);
	void globalToneMap();
	void noToneMap(); // technically it is tone mapping, but just clip ouside [0, 255]

	void blur(int radius, int times);
	void blur(HdrPixel* hdrPixels, int radius, int times);
	// assumes you've allocated dstPixels
	void blur(HdrPixel* srcPixels, HdrPixel* dstPixels, int radius, int times);
	
	void bloom(bool selective=false, int times=1);
	void bloom(HdrPixel* hdrPixels, bool selective=false, int times=1);
	void bloom(HdrPixel* srcPixels, HdrPixel* dstPixels, bool selective=false, int times=1);

private:

	Image m_image;
    HdrPixel* m_hdrPixels;
    int m_width;
    int m_height;
	float m_scaleFactor;
	float m_toneMapStrength;
};

#endif // CSE168_IMAGE_H_INCLUDED
